package com.haiyou.data.common.persister.mysql.help;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.Validate;

import com.google.common.collect.Lists;
import com.haiyou.common.entity.Entity;
import com.haiyou.common.util.collect.SortUtils;
import com.haiyou.data.common.persister.mysql.meta.DatabaseMeta;
import com.haiyou.data.common.persister.mysql.meta.TableMeta;
import com.haiyou.data.common.persister.mysql.persister.Persister;
import com.haiyou.data.common.persister.mysql.annotation.Table.RollPolicy;
import com.haiyou.data.common.persister.mysql.annotation.Index.IndexPolicy;

/**
 * 读取滚表的二次封装，根据先后顺序，自动limit截取,按时间读取数据等
 * 
 * @author Administrator
 *
 */
public class RollTableQueryHelper {
	/**
	 * 按条件读取mysql滚表数据，按开始时间和结束时间筛选合适的表读取,尽量缩短startTime 到endTime的间距，使结果数据量不至于太大
	 * 
	 * @param <Id>
	 * @param <E>
	 * @param entityClass
	 * @param startTime
	 * @param endTime
	 * @return
	 */
	public static <Id extends Serializable & Comparable<Id>, E extends Entity<Id>> List<E> queryByTimes(
			Class<E> entityClass, Date startTime, Date endTime) {
		return queryByTimes(entityClass, null, startTime, endTime);
	}

	/**
	 * 按条件读取mysql滚表数据，按开始时间和结束时间筛选合适的表读取,尽量缩短startTime
	 * 到endTime的间距，并结合condition使结果数据量不至于太大
	 * 
	 * @param <Id>
	 * @param <E>
	 * @param entityClass
	 * @param condition
	 * @param startTime
	 * @param endTime
	 * @return
	 */
	public static <Id extends Serializable & Comparable<Id>, E extends Entity<Id>> List<E> queryByTimes(
			Class<E> entityClass, String condition, Date startTime, Date endTime) {
		TableMeta tableMeta = TableHelper.getTableMeta(entityClass);
		Validate.isTrue(tableMeta.getRollPolicy() != RollPolicy.NONE);

		DatabaseMeta databaseMeta = DatabaseHelper.getDatabaseMeta(entityClass);
		String db = DatabaseHelper.getOnlyReallyName(databaseMeta);

		Persister<Id, E> persister = Persister.getInstance(entityClass);

		List<String> tables = Lists.newArrayList(TableHelper.allTables(tableMeta));

		List<String> queryTables = Lists.newArrayList();
		if (startTime == null && endTime != null) {
			queryTables
					.add(RollPolicy.parseName(tableMeta.getSimpleName(), tableMeta.getRollPolicy(), endTime.getTime()));
		} else if (startTime != null && endTime == null) {
			queryTables.add(
					RollPolicy.parseName(tableMeta.getSimpleName(), tableMeta.getRollPolicy(), startTime.getTime()));
		} else if (startTime != null && endTime != null) {
			Validate.isTrue(startTime.compareTo(endTime) < 0);
			String t1 = RollPolicy.parseName(tableMeta.getSimpleName(), tableMeta.getRollPolicy(), startTime.getTime());
			String t2 = RollPolicy.parseName(tableMeta.getSimpleName(), tableMeta.getRollPolicy(), endTime.getTime());
			tables.forEach(tab -> {
				if (tab.compareTo(t1) == 0 || tab.compareTo(t2) == 0) {
					queryTables.add(tab);
				}
			});
		}

		List<E> result = Lists.newArrayList();
		if (condition != null)
			queryTables.forEach(
					tab -> result.addAll(persister.queryByConditionLimits(db, tab, condition, 0, Integer.MAX_VALUE)));
		else
			queryTables.forEach(tab -> result.addAll(persister.queryByLimits(db, tab, 0, Integer.MAX_VALUE, false)));

		return result;
	}

	/**
	 * 按条件读取mysql滚表数据，开始位置和结束位置会根据升序或者倒序逐个表去查询，一直到元素数量满足（endIndex-startIndex） 为止
	 * 
	 * <pre>
	 * 例如：如下是按日期滚动生成的表，如果每个表里面有100条数据，需要查询150-250之间的数据，那么
	 * &#64;_t_logs_modify_property_20211120
	 * &#64;_t_logs_modify_property_20211121
	 * &#64;_t_logs_modify_property_20211122
	 * &#64;_t_logs_modify_property_20211123
	 * &#64;_t_logs_modify_property_20211124
	 * 升序就是
	 * &#64;_t_logs_modify_property_20211121 - @_t_logs_modify_property_20211122
	 * 降序就是
	 * &#64;_t_logs_modify_property_20211124 - @_t_logs_modify_property_20211123
	 * </pre>
	 * 
	 * @param <Id>        主键类型
	 * @param <E>         实体类型
	 * @param entityClass 实体类型
	 * @param condition   条件
	 * @param startIndex  开始位置
	 * @param endIndex    结束位置
	 * @param reversed
	 * @return
	 */
	public static <Id extends Serializable & Comparable<Id>, E extends Entity<Id>> List<E> queryByLimits(
			Class<E> entityClass, int startIndex, int endIndex, boolean reversed) {
		return queryByConditionLimitsV2(entityClass, null, startIndex, endIndex, reversed);
	}

	/**
	 * 按条件读取mysql滚表数据，开始位置和结束位置会根据升序或者倒序逐个表去查询，一直到元素数量满足（endIndex-startIndex） 为止
	 * 
	 * <pre>
	 * 例如：如下是按日期滚动生成的表，如果每个表里面有100条数据，需要查询150-250之间的数据，那么
	 * &#64;_t_logs_modify_property_20211120
	 * &#64;_t_logs_modify_property_20211121
	 * &#64;_t_logs_modify_property_20211122
	 * &#64;_t_logs_modify_property_20211123
	 * &#64;_t_logs_modify_property_20211124
	 * 升序就是
	 * &#64;_t_logs_modify_property_20211121 - @_t_logs_modify_property_20211122
	 * 降序就是
	 * &#64;_t_logs_modify_property_20211124 - @_t_logs_modify_property_20211123
	 * </pre>
	 * 
	 * @param <Id>        主键类型
	 * @param <E>         实体类型
	 * @param entityClass 实体类型
	 * @param condition   条件
	 * @param startIndex  开始位置
	 * @param endIndex    结束位置
	 * @param reversed    是否是倒序,如果condition条件不为空，那么倒序所需的sql需要自行组装到condition中去，比如
	 *                    order by `id` desc
	 * @return
	 */
	@Deprecated
	public static <Id extends Serializable & Comparable<Id>, E extends Entity<Id>> List<E> queryByConditionLimits(
			Class<E> entityClass, String condition, int startIndex, int endIndex, boolean reversed) {

		TableMeta tableMeta = TableHelper.getTableMeta(entityClass);
		Validate.isTrue(tableMeta.getRollPolicy() != RollPolicy.NONE);

		DatabaseMeta databaseMeta = DatabaseHelper.getDatabaseMeta(entityClass);
		String db = DatabaseHelper.getOnlyReallyName(databaseMeta);

		Persister<Id, E> persister = Persister.getInstance(entityClass);

		List<String> tables = Lists.newArrayList(TableHelper.allTables(tableMeta));
		SortUtils.quickSort(tables, 0, tables.size() - 1, (s1, s2) -> reversed ? s2.compareTo(s1) : s1.compareTo(s2));

		startIndex = Math.min(startIndex, endIndex);
		endIndex = Math.max(startIndex, endIndex);

		int capacity = endIndex - startIndex;
		List<E> result = Lists.newArrayListWithCapacity(capacity);

		int sum = 0;
		int beginTableIndex = 0;
		String beginTable = null;
		int beginIndex = 0;
		for (int i = 0; i < tables.size(); i++) {
			String tab = tables.get(i);
			int count = persister.count(db, tab);
			sum += count;
			if (startIndex <= sum) {
				beginTableIndex = i;
				beginTable = tab;
				beginIndex = count - (sum - startIndex);
				break;
			}
		}

		if (beginTable != null) {
			queryFromTableByConditionLimits(result, persister, db, beginTable, condition, beginIndex,
					beginIndex + capacity - result.size(), reversed);
			while (result.size() < capacity && beginTableIndex < tables.size() - 1) {
				beginTableIndex++;
				beginTable = tables.get(beginTableIndex);
				beginIndex = 0;
				queryFromTableByConditionLimits(result, persister, db, beginTable, condition, beginIndex,
						beginIndex + capacity - result.size(), reversed);
			}
		}

		return result;
	}
	
	
	public static <Id extends Serializable & Comparable<Id>, E extends Entity<Id>> List<E> queryByConditionLimitsV2(
			Class<E> entityClass, String condition, int startIndex, int endIndex, boolean reversed) {

		TableMeta tableMeta = TableHelper.getTableMeta(entityClass);
		Validate.isTrue(tableMeta.getRollPolicy() != RollPolicy.NONE);

		DatabaseMeta databaseMeta = DatabaseHelper.getDatabaseMeta(entityClass);
		String db = DatabaseHelper.getOnlyReallyName(databaseMeta);

		Persister<Id, E> persister = Persister.getInstance(entityClass);

		List<String> tables = Lists.newArrayList(TableHelper.allTables(tableMeta));
		SortUtils.quickSort(tables, 0, tables.size() - 1, (s1, s2) -> reversed ? s2.compareTo(s1) : s1.compareTo(s2));

		startIndex = Math.min(startIndex, endIndex);
		endIndex = Math.max(startIndex, endIndex);

		int capacity = endIndex - startIndex;
		List<E> result = Lists.newArrayListWithCapacity(capacity);

		int sum = 0;
		int beginTableIndex = 0;
		String beginTable = null;
		int beginIndex = 0;
		for (int i = 0; i < tables.size(); i++) {
			String tab = tables.get(i);
//			int count = persister.count(db, tab);
			
			beginTable = tab;
			beginTableIndex = i;
			beginIndex = startIndex;
			break;
//			sum += count;
//			if (startIndex <= sum) {
//				beginTableIndex = i;
//				beginTable = tab;
//				beginIndex = count - (sum - startIndex);
//				break;
//			}
		}

		if (beginTable != null) {
			queryFromTableByConditionLimits(result, persister, db, beginTable, condition, beginIndex,
					beginIndex + capacity - result.size(), reversed);
			// 先从最新的表中查询是否包含指定索引的数据，如果当前表包含 直接返回
			if (result.size() >= capacity) {
				return result;
			}
			result = new ArrayList<>();
			// 最新的表数据不全,依次循环滚表中获取
			beginIndex = 0;
			while (result.size() < endIndex && beginTableIndex < tables.size()) {
				beginTable = tables.get(beginTableIndex);
				queryFromTableByConditionLimits(result, persister, db, beginTable, condition, beginIndex,
						beginIndex + endIndex - result.size(), reversed);
				beginTableIndex++;
			}
		}
		if (result.size() >= startIndex) {
			return result.subList(startIndex, Math.min(result.size(), endIndex));
		}
		return Lists.newArrayList();
	}

	protected static <Id extends Serializable & Comparable<Id>, E extends Entity<Id>> void queryFromTableByConditionLimits(
			List<E> result, Persister<Id, E> persister, String db, String tab, String condition, int startIndex,
			int endIndex, boolean reversed) {
		
		result.addAll(condition == null ? persister.queryByLimits(db, tab, startIndex, endIndex, reversed)
				: persister.queryByConditionLimits(db, tab, condition, startIndex, endIndex));
	}
}
